﻿using System;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AIProject.Search;

namespace AIProject.AI
{
    class IDAlphaBetaGameAgent : GameAgent
    {
        private Boolean isWhite;
        private AlphaBeta ab;
        private NodeInfo nodeInfo;
        private Pylos.Action maxAction;
        private Pylos.Action minAction;
        private Percept p;
        private Boolean stop;

        public int Nodes
        {
            get
            {
                return (ab.NodeCount + ab.ExtNodes);
            }
            set
            {
            }
        }

        public IDAlphaBetaGameAgent(Boolean isWhite)
        {
            this.isWhite = isWhite;
            this.stop = false;
        }

        public Pylos.Action getAction(Percept p)
        {
            this.nodeInfo = new NodeInfo(1, isWhite);
            this.ab = new AlphaBeta(nodeInfo, 4);
            this.p = p;
            this.stop = false;

            Thread t = new Thread(doGetAction);
            Stopwatch sw = new Stopwatch();

            t.Start();
            sw.Start();

            while (!t.IsAlive && sw.ElapsedMilliseconds < 3000) { }

            sw.Stop();

            if (sw.ElapsedMilliseconds > 3000)
            {
                throw new InvalidOperationException("The getActions() Thread took too long to start");
            }
            else
            {
                sw.Reset();
            }

            sw.Start();

            while (sw.ElapsedMilliseconds < 3000 && t.IsAlive) { }

            ab.Stop = true;
            this.stop = true;

            return (isWhite ? maxAction : minAction);
        }

        public void doGetAction()
        {
            Node root = new Node(p.State);
            List<Pylos.Action> actions = p.State.getActions();

            while (nodeInfo.DepthLimit <= 4)
            {
                if (stop)
                {
                    return;
                }

                double alpha = Double.NegativeInfinity;
                double beta = Double.PositiveInfinity;
                double childValue;

                foreach (Pylos.Action arc in actions)
                {
                    if (stop)
                    {
                        return;
                    }

                    Node child = (Node)root.clone();
                    child.update(arc);

                    if (this.isWhite)
                    {
                        if (nodeInfo.isTerminal(child)
                                || child.State.getActions().Count == 0
                                || child.Cost >= nodeInfo.DepthLimit)
                        {
                            childValue = nodeInfo.utility(child);
                            ab.NodeCount++;
                        }
                        else
                        {
                            childValue = ab.minValue(child, alpha, beta, false, false);
                        }

                        if (childValue >= alpha)
                        {
                            alpha = childValue;
                            maxAction = arc;
                        }
                    }
                    else
                    {
                        if (nodeInfo.isTerminal(child)
                                || child.State.getActions().Count == 0
                                || child.Cost >= nodeInfo.DepthLimit)
                        {
                            ab.NodeCount++;
                            childValue = nodeInfo.utility(child);
                        }
                        else
                        {
                            childValue = ab.maxValue(child, alpha, beta, false, false);
                        }

                        if (childValue <= beta)
                        {
                            beta = childValue;
                            minAction = arc;
                        }
                    }
                }
                //Console.WriteLine(ab.NodeCount + " for " + nodeInfo.DepthLimit + " and ext Nodes " + ab.ExtNodes);
                nodeInfo.DepthLimit++;

                if (nodeInfo.DepthLimit < 5)
                {
                    ab.reset();
                }
            }
        }
    }
}
